package com.asdhawaii.otello.players;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import com.asdhawaii.otello.board.Direction;
import com.asdhawaii.otello.board.Move;
import com.asdhawaii.otello.board.Square;
import com.asdhawaii.otello.board.SquareContents;

/**
 * Tests the methods of the AiStrategies class.
 * 
 * @author Branden Ogata
 *
 */

public class TestAiStrategies
{
  /**
   * Tests the isValidMove method in the AiStrategies class.
   * 
   */
  
  @Test
  public void testIsValidMove()
  {
    // Test if valid move looking in valid direction is valid
    Square[][] board = TestAbstractOtelloAi.createStartingBoard();
    assertTrue("Test valid move, valid direction", AiStrategies.isValidMove(board, 3, 4, 
                                                                            Direction.S, "B"));
    
    // Test if valid move looking in invalid direction is invalid
    assertFalse("Test valid move, invalid direction", AiStrategies.isValidMove(board, 3, 4, 
                                                                               Direction.E, "B"));
    
    // Test if invalid move is invalid
    assertFalse("Test invalid move", AiStrategies.isValidMove(board, 0, 0, Direction.E, "B"));
    
    // Test if move collinear with what would be a valid move is invalid
    board[4][2].setContents(SquareContents.BLACK);
    assertFalse("Test invalid move on line with valid move", AiStrategies.isValidMove(board, 1, 4, 
                                                                                      Direction.S, 
                                                                                      "B"));
  }

  /**
   * Tests the getFlips method in the AiStrategies class.
   * 
   */
  
  @Test
  public void testGetFlips()
  {
    Square[][] board = TestAbstractOtelloAi.createStartingBoard();
    
    // Test a move that does not flip any pieces
    assertEquals("Test move that does not flip any pieces", 
                 0, AiStrategies.getFlips(board, 0, 0, Direction.SE, SquareContents.BLACK));
    
    // Test a move that flips one piece
    assertEquals("Test move that flips one piece", 
                 1, AiStrategies.getFlips(board, 3, 4, Direction.W, SquareContents.BLACK));
    
    // Test a move that flips multiple pieces in a row
    board[3][5].setContents(SquareContents.WHITE);
    assertEquals("Test move that flips two pieces",
                 2, AiStrategies.getFlips(board, 3, 5, Direction.W, SquareContents.BLACK));
  }
  
  /**
   * Tests the random method in the AiStrategies class.
   * 
   */
  
  @Test
  public void testRandom()
  {
    boolean playedTwoFour = false;
    boolean playedThreeFive = false;
    boolean playedFourTwo = false;
    boolean playedFiveThree = false;
    
    List<Move> validMoves = new ArrayList<Move>();
    TestAbstractOtelloAi.createList(validMoves, new Move(2, 4), new Move(3, 5), 
                                                new Move(4, 2), new Move(5, 3));
    
    for (int i = 0; i < 64; i++)
    {
      Move move = AiStrategies.random(validMoves);
      
      assertTrue("Random selects valid move", validMoves.contains(move));
      
      switch (move.getRow())
      {
        case 2:
          playedTwoFour = true;
          break;
        case 3: 
          playedThreeFive = true;
          break;
        case 4: 
          playedFourTwo = true;
          break;
        case 5:
          playedFiveThree = true;
          break;
        default:
          fail("Invalid move selected");
          break;
      }
    }
    
    assertTrue("Random played (2, 4)", playedTwoFour);
    assertTrue("Random played (3, 5)", playedThreeFive);
    assertTrue("Random played (4, 2)", playedFourTwo);
    assertTrue("Random played (5, 3)", playedFiveThree);    
  }
  
  /**
   * Tests the maxima method in the AiStrategies class.
   * 
   */
  
  @Test
  public void testMaxima()
  {
    // Test with starting board
    Square[][] board = TestAbstractOtelloAi.createStartingBoard();
    
    List<Move> validMoves = new ArrayList<Move>();
    TestAbstractOtelloAi.createList(validMoves, new Move(4, 2), new Move(5, 3), 
                                    new Move(3, 5), new Move(2, 4));
    
    assertTrue("Selects valid move from equivalents", 
               validMoves.containsAll(AiStrategies.maxima(board, validMoves, 
                                                          SquareContents.BLACK)));
    
    // Test board with only one valid move
    board[4][2].setContents(SquareContents.BLACK);
    board[5][3].setContents(SquareContents.BLACK);
    board[3][5].setContents(SquareContents.BLACK);
   
    TestAbstractOtelloAi.createList(validMoves, new Move(2, 4));
    
    assertTrue("Selects only valid move", 
               validMoves.containsAll(AiStrategies.maxima(board, validMoves, 
                                                          SquareContents.BLACK)));
    
    // Test board with no valid moves
    board[2][4].setContents(SquareContents.BLACK);
    
    validMoves.clear();
    
    assertTrue("Returns empty list without valid move", 
               AiStrategies.maxima(board, validMoves, SquareContents.BLACK).isEmpty());        
    
    // Tests that the AI selects one of the moves that flips the most pieces
    board = TestAbstractOtelloAi.createStartingBoard();
    board[4][3].setContents(SquareContents.BLACK);
    board[5][3].setContents(SquareContents.BLACK);
    board[4][4].setContents(SquareContents.WHITE);
    board[5][4].setContents(SquareContents.WHITE);
    
    TestAbstractOtelloAi.createList(validMoves, new Move(2, 5), new Move(3, 5), 
                                    new Move(4, 5), new Move(5, 5), new Move(6, 5));
    
    List<Move> expectedMoves = new ArrayList<Move>();
    TestAbstractOtelloAi.createList(expectedMoves, new Move(3, 5), new Move(5, 5));

    assertTrue("Selects a move that flips the most pieces", 
               expectedMoves.containsAll(AiStrategies.maxima(board, validMoves, 
                                                             SquareContents.BLACK)));
    
    // Tests that the AI selects the one move that flips the most pieces
    board[5][5].setContents(SquareContents.BLACK);
    board[4][4].setContents(SquareContents.BLACK);
    board[5][4].setContents(SquareContents.BLACK);
    board[6][4].setContents(SquareContents.WHITE);
    board[5][4].setContents(SquareContents.WHITE);
    board[4][4].setContents(SquareContents.WHITE);
    
    TestAbstractOtelloAi.createList(validMoves, new Move(2, 3), new Move(2, 5), new Move(3, 5), 
                                    new Move(4, 5), new Move(6, 5), new Move(7, 5), new Move(7, 3));
    
    assertTrue("Select the move that flips the most pieces", 
               AiStrategies.maxima(board, validMoves, 
                                   SquareContents.BLACK).contains(new Move(3, 5)));    
  }
  
  /**
   * Tests the getCorners method in the AiStrategies class.
   * 
   */
  
  @Test
  public void testGetCorners()
  {    
    Square[][] board = TestAbstractOtelloAi.createEmptyBoard();
    
    // Call getCorners 
    List<Move> results = AiStrategies.getCorners(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);
    
    // Result should only have the four corners
    assertTrue("Contains 00", results.contains(new Move(0, 0)));
    assertTrue("Contains 07", results.contains(new Move(0, 7)));
    assertTrue("Contains 70", results.contains(new Move(7, 0)));
    assertTrue("Contains 77", results.contains(new Move(7, 7)));
    assertEquals("Contains only four corners", 4, results.size());
  }
  
  /**
   * Tests the getASquares method in the AiStrategies class.
   *  
   */
  
  @Test
  public void testGetASquares()
  {    
    Square[][] board = TestAbstractOtelloAi.createEmptyBoard();
    
    // Call getASquares 
    List<Move> results = AiStrategies.getASquares(board, TestAiStrategies.allMoves(board), 
                                                  SquareContents.BLACK);
    
    // Result should only have the eight A squares
    assertTrue("Contains 02", results.contains(new Move(0, 2)));
    assertTrue("Contains 05", results.contains(new Move(0, 5)));
    assertTrue("Contains 20", results.contains(new Move(2, 0)));
    assertTrue("Contains 27", results.contains(new Move(2, 7)));
    assertTrue("Contains 50", results.contains(new Move(5, 0)));
    assertTrue("Contains 57", results.contains(new Move(5, 7)));
    assertTrue("Contains 72", results.contains(new Move(7, 2)));
    assertTrue("Contains 75", results.contains(new Move(7, 5)));
    assertEquals("Contains only eight A squares", 8, results.size());
  }
  
  /**
   * Tests the getCSquares method in the AiStrategies class.
   *  
   */
  
  @Test
  public void testGetCSquares()
  {    
    Square[][] board = TestAbstractOtelloAi.createEmptyBoard();
    
    // Call getCSquares 
    List<Move> results = AiStrategies.getCSquares(board, TestAiStrategies.allMoves(board), 
                                                  SquareContents.BLACK);
    
    // Result should only have the eight C squares
    assertTrue("Contains 01", results.contains(new Move(0, 1)));
    assertTrue("Contains 06", results.contains(new Move(0, 6)));
    assertTrue("Contains 10", results.contains(new Move(1, 0)));
    assertTrue("Contains 17", results.contains(new Move(1, 7)));
    assertTrue("Contains 60", results.contains(new Move(6, 0)));
    assertTrue("Contains 67", results.contains(new Move(6, 7)));
    assertTrue("Contains 71", results.contains(new Move(7, 1)));
    assertTrue("Contains 76", results.contains(new Move(7, 6)));
    assertEquals("Contains only eight C squares", 8, results.size());
  }

  /**
   * Tests the getXSquares method in the AiStrategies class.
   *  
   */
  
  @Test
  public void testGetXSquares()
  {    
    Square[][] board = TestAbstractOtelloAi.createEmptyBoard();
    
    // Call getXSquares
    List<Move> results = AiStrategies.getXSquares(board, TestAiStrategies.allMoves(board), 
                                                  SquareContents.BLACK);
    
    // Result should only have the four X squares
    assertTrue("Contains 11", results.contains(new Move(1, 1)));
    assertTrue("Contains 16", results.contains(new Move(1, 6)));
    assertTrue("Contains 61", results.contains(new Move(6, 1)));
    assertTrue("Contains 66", results.contains(new Move(6, 6)));
    assertEquals("Contains only four X squares", 4, results.size());
  }
  
  /**
   * Tests the getContiguousCSquares method in the AiStrategies class.
   *  
   */
  
  @Test
  public void testGetContiguousCSquares()
  {    
    Square[][] board = TestAbstractOtelloAi.createEmptyBoard();
    
    // Call getContiguousCSquares
    List<Move> results = AiStrategies.getContiguousCSquares(board, 
                                                            TestAiStrategies.allMoves(board), 
                                                            SquareContents.BLACK);
    
    // Result should be empty since all of the corners are empty
    assertTrue("Empty board does not have contiguous C squares", results.isEmpty());

    // 00 corner
    board[0][0].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousCSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);

    // Result should only have the two C squares adjacent to 00
    assertTrue("Contains 01", results.contains(new Move(0, 1)));
    assertFalse("Contains 06", results.contains(new Move(0, 6)));
    assertTrue("Contains 10", results.contains(new Move(1, 0)));
    assertFalse("Contains 17", results.contains(new Move(1, 7)));
    assertFalse("Contains 60", results.contains(new Move(6, 0)));
    assertFalse("Contains 67", results.contains(new Move(6, 7)));
    assertFalse("Contains 71", results.contains(new Move(7, 1)));
    assertFalse("Contains 76", results.contains(new Move(7, 6)));
    assertEquals("Contains only two C squares adjacent to 00", 2, results.size());

    board[0][0].setContents(SquareContents.EMPTY);
    
    // 07 corner
    board[0][7].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousCSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);

    // Result should only have the two C squares adjacent to 07
    assertFalse("Contains 01", results.contains(new Move(0, 1)));
    assertTrue("Contains 06", results.contains(new Move(0, 6)));
    assertFalse("Contains 10", results.contains(new Move(1, 0)));
    assertTrue("Contains 17", results.contains(new Move(1, 7)));
    assertFalse("Contains 60", results.contains(new Move(6, 0)));
    assertFalse("Contains 67", results.contains(new Move(6, 7)));
    assertFalse("Contains 71", results.contains(new Move(7, 1)));
    assertFalse("Contains 76", results.contains(new Move(7, 6)));
    assertEquals("Contains only two C squares adjacent to 07", 2, results.size());

    board[0][7].setContents(SquareContents.EMPTY);
    
    // 70 corner
    board[7][0].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousCSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);

    // Result should only have the two C squares adjacent to 70
    assertFalse("Contains 01", results.contains(new Move(0, 1)));
    assertFalse("Contains 06", results.contains(new Move(0, 6)));
    assertFalse("Contains 10", results.contains(new Move(1, 0)));
    assertFalse("Contains 17", results.contains(new Move(1, 7)));
    assertTrue("Contains 60", results.contains(new Move(6, 0)));
    assertFalse("Contains 67", results.contains(new Move(6, 7)));
    assertTrue("Contains 71", results.contains(new Move(7, 1)));
    assertFalse("Contains 76", results.contains(new Move(7, 6)));
    assertEquals("Contains only two C squares adjacent to 70", 2, results.size());

    board[7][0].setContents(SquareContents.EMPTY);

    // 77 corner
    board[7][7].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousCSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);

    // Result should only have the two C squares adjacent to 77
    assertFalse("Contains 01", results.contains(new Move(0, 1)));
    assertFalse("Contains 06", results.contains(new Move(0, 6)));
    assertFalse("Contains 10", results.contains(new Move(1, 0)));
    assertFalse("Contains 17", results.contains(new Move(1, 7)));
    assertFalse("Contains 60", results.contains(new Move(6, 0)));
    assertTrue("Contains 67", results.contains(new Move(6, 7)));
    assertFalse("Contains 71", results.contains(new Move(7, 1)));
    assertTrue("Contains 76", results.contains(new Move(7, 6)));
    assertEquals("Contains only two C squares adjacent to 77", 2, results.size());

    board[7][7].setContents(SquareContents.EMPTY);
    
    // All four corners together
    board = TestAbstractOtelloAi.createEmptyBoard();
    board[0][0].setContents(SquareContents.BLACK);
    board[0][7].setContents(SquareContents.BLACK);
    board[7][0].setContents(SquareContents.BLACK);
    board[7][7].setContents(SquareContents.BLACK);
    
    // Call getContiguousCSquares again
    results = AiStrategies.getContiguousCSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);
    
    // Result should only have the eight C squares
    assertTrue("Contains 01", results.contains(new Move(0, 1)));
    assertTrue("Contains 06", results.contains(new Move(0, 6)));
    assertTrue("Contains 10", results.contains(new Move(1, 0)));
    assertTrue("Contains 17", results.contains(new Move(1, 7)));
    assertTrue("Contains 60", results.contains(new Move(6, 0)));
    assertTrue("Contains 67", results.contains(new Move(6, 7)));
    assertTrue("Contains 71", results.contains(new Move(7, 1)));
    assertTrue("Contains 76", results.contains(new Move(7, 6)));
    assertEquals("Contains only eight C squares", 8, results.size());
  }

  /**
   * Tests the getContiguousCSquares method in the AiStrategies class.
   *  
   */
  
  @Test
  public void testGetContiguousXSquares()
  {    
    Square[][] board = TestAbstractOtelloAi.createEmptyBoard();
    
    // Call getContiguousXSquares
    List<Move> results = AiStrategies.getContiguousXSquares(board, 
                                                            TestAiStrategies.allMoves(board), 
                                                            SquareContents.BLACK);
    
    // Result should be empty since all of the corners are empty
    assertTrue("Empty board does not have contiguous X squares", results.isEmpty());
    
    // 00 corner
    board[0][0].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousXSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);    

    // Result should only have the X square adjacent to 00
    assertTrue("Contains 11", results.contains(new Move(1, 1)));
    assertFalse("Contains 16", results.contains(new Move(1, 6)));
    assertFalse("Contains 61", results.contains(new Move(6, 1)));
    assertFalse("Contains 66", results.contains(new Move(6, 6)));
    assertEquals("Contains only one X square adjacent to 00", 1, results.size());
    
    board[0][0].setContents(SquareContents.EMPTY);
    
    // 07 corner
    board[0][7].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousXSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);    

    // Result should only have the X square adjacent to 07
    assertFalse("Contains 11", results.contains(new Move(1, 1)));
    assertTrue("Contains 16", results.contains(new Move(1, 6)));
    assertFalse("Contains 61", results.contains(new Move(6, 1)));
    assertFalse("Contains 66", results.contains(new Move(6, 6)));
    assertEquals("Contains only one X square adjacent to 07", 1, results.size());

    board[0][7].setContents(SquareContents.EMPTY);
    
    // 70 corner
    board[7][0].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousXSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);    

    // Result should only have the X square adjacent to 70
    assertFalse("Contains 11", results.contains(new Move(1, 1)));
    assertFalse("Contains 16", results.contains(new Move(1, 6)));
    assertTrue("Contains 61", results.contains(new Move(6, 1)));
    assertFalse("Contains 66", results.contains(new Move(6, 6)));
    assertEquals("Contains only one X square adjacent to 70", 1, results.size());

    board[7][0].setContents(SquareContents.EMPTY);
    
    // 77 corner
    board[7][7].setContents(SquareContents.BLACK);
    results = AiStrategies.getContiguousXSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);    

    // Result should only have the X square adjacent to 77
    assertFalse("Contains 11", results.contains(new Move(1, 1)));
    assertFalse("Contains 16", results.contains(new Move(1, 6)));
    assertFalse("Contains 61", results.contains(new Move(6, 1)));
    assertTrue("Contains 66", results.contains(new Move(6, 6)));
    assertEquals("Contains only one X square adjacent to 77", 1, results.size());
    
    board[7][7].setContents(SquareContents.EMPTY);

    // All four corners together
    board[0][0].setContents(SquareContents.BLACK);
    board[0][7].setContents(SquareContents.BLACK);
    board[7][0].setContents(SquareContents.BLACK);
    board[7][7].setContents(SquareContents.BLACK);
    
    // Call getContiguousXSquares again
    results = AiStrategies.getContiguousXSquares(board, TestAiStrategies.allMoves(board), 
                                                 SquareContents.BLACK);
    
    // Result should only have the four X squares
    assertTrue("Contains 11", results.contains(new Move(1, 1)));
    assertTrue("Contains 16", results.contains(new Move(1, 6)));
    assertTrue("Contains 61", results.contains(new Move(6, 1)));
    assertTrue("Contains 66", results.contains(new Move(6, 6)));
    assertEquals("Contains only four X squares", 4, results.size());
  }  
  
  /**
   * Tests the minima method in the AiStrategies class.
   * 
   */
  
  @Test
  public void testMinima()
  {
    Square[][] board = TestAbstractOtelloAi.createStartingBoard();
    List<Move> validMoves = new ArrayList<Move>();
    
    TestAbstractOtelloAi.createList(validMoves, new Move(4, 2), new Move(5, 3), 
                                    new Move(3, 5), new Move(2, 4));
    
    assertTrue("Selects valid move from equivalents", 
               validMoves.containsAll(AiStrategies.minima(board, validMoves, 
                                                          SquareContents.BLACK)));
    
    // Test board with only one valid move
    board[4][2].setContents(SquareContents.BLACK);
    board[5][3].setContents(SquareContents.BLACK);
    board[3][5].setContents(SquareContents.BLACK);
   
    TestAbstractOtelloAi.createList(validMoves, new Move(2, 4));
    
    assertTrue("Selects only valid move", 
               validMoves.containsAll(AiStrategies.minima(board, validMoves, 
                                                          SquareContents.BLACK)));
    
    // Test board with no valid moves
    board[2][4].setContents(SquareContents.BLACK);
    
    validMoves.clear();
    
    assertTrue("Returns empty list without valid move", 
               AiStrategies.minima(board, validMoves, SquareContents.BLACK).isEmpty());
    
    // Tests that minima selects one of the moves that flips the fewest pieces
    board = TestAbstractOtelloAi.createStartingBoard();
    board[4][3].setContents(SquareContents.BLACK);
    board[5][3].setContents(SquareContents.BLACK);
    board[4][4].setContents(SquareContents.WHITE);
    board[5][4].setContents(SquareContents.WHITE);
    
    TestAbstractOtelloAi.createList(validMoves, new Move(2, 5), new Move(3, 5), 
                                    new Move(4, 5), new Move(5, 5), new Move(6, 5));
    
    List<Move> expectedMoves = new ArrayList<Move>();
    TestAbstractOtelloAi.createList(expectedMoves, new Move(2, 5), new Move(4, 5), new Move(6, 5));

    assertTrue("Selects a move that flips the fewest pieces", 
               expectedMoves.containsAll(AiStrategies.minima(board, validMoves, 
                                                             SquareContents.BLACK)));
    
    // Tests that the AI selects the one move that flips the fewest pieces
    board[2][3].setContents(SquareContents.BLACK);
    board[4][3].setContents(SquareContents.WHITE);
    board[4][5].setContents(SquareContents.BLACK);
    
    TestAbstractOtelloAi.createList(validMoves, new Move(3, 5), new Move(5, 5), new Move(6, 3));
    
    assertTrue("Select the move that flips the fewest pieces", 
                 AiStrategies.minima(board, validMoves, 
                                     SquareContents.BLACK).contains(new Move(6, 3)));    
  }
  
  /**
   * Tests the stableMoves method in the AiStrategies class.
   * 
   */
  
  @Test
  public void testStableMoves()
  {
    Square[][] board = TestAbstractOtelloAi.createStartingBoard();
    List<Move> validMoves = new ArrayList<Move>();
    
    TestAbstractOtelloAi.createList(validMoves, new Move(4, 2), new Move(5, 3), 
                                    new Move(3, 5), new Move(2, 4));
    
    // None of the moves are stable
    assertTrue("Returns empty when no stable moves exist",
               AiStrategies.stableMoves(board, validMoves, SquareContents.BLACK).isEmpty());
    
    // Pieces along edge, not stable (WBBWx...)
    board = TestAbstractOtelloAi.createEmptyBoard();
    board[0][0].setContents(SquareContents.WHITE);
    board[0][1].setContents(SquareContents.BLACK);
    board[0][2].setContents(SquareContents.BLACK);
    board[0][3].setContents(SquareContents.WHITE);
    
    TestAbstractOtelloAi.createList(validMoves, new Move(0, 4));
    
    assertTrue("Returns empty when no stable moves exist: White/Empty after move",
               AiStrategies.stableMoves(board, validMoves, SquareContents.BLACK).isEmpty());
    
    // Pieces along edge, not stable (.BBWx...)
    board[0][0].setContents(SquareContents.EMPTY);

    assertTrue("Returns empty when no stable moves exist: Empty/Empty after move",
               AiStrategies.stableMoves(board, validMoves, SquareContents.BLACK).isEmpty());

    // Pieces along edge, not stable (....x...)
    board = TestAbstractOtelloAi.createEmptyBoard();
    board[1][4].setContents(SquareContents.WHITE);
    board[2][4].setContents(SquareContents.BLACK);
    
    assertTrue("Returns empty when no stable moves exist: Empty/Empty before move",
               AiStrategies.stableMoves(board, validMoves, SquareContents.BLACK).isEmpty());
    
    // Pieces along edge, stable (BWx.....)
    board[0][0].setContents(SquareContents.BLACK);
    board[0][1].setContents(SquareContents.WHITE);
    
    validMoves.add(new Move(0, 2));
    
    assertTrue("Returns stable move: Black/Empty",
               AiStrategies.stableMoves(board, validMoves, 
                                        SquareContents.BLACK).contains(new Move(0, 2)));
    
    // Pieces along edge, stable (BWxW....)
    board[0][3].setContents(SquareContents.WHITE);
    
    assertTrue("Returns stable move: Black/White",
               AiStrategies.stableMoves(board, validMoves, 
                                        SquareContents.BLACK).contains(new Move(0, 2)));
    
    // Pieces along edge, stable (BWWWWWWx)
    board[0][2].setContents(SquareContents.WHITE);
    board[0][4].setContents(SquareContents.WHITE);
    board[0][5].setContents(SquareContents.WHITE);
    board[0][6].setContents(SquareContents.WHITE);

    TestAbstractOtelloAi.createList(validMoves, new Move(0, 7));

    assertTrue("Returns stable move: Black/Edge",
               AiStrategies.stableMoves(board, validMoves, 
                                        SquareContents.BLACK).contains(new Move(0, 7)));
    
    // Last move, stable
    board = TestAbstractOtelloAi.createEmptyBoard();
    board[0][0].setContents(SquareContents.BLACK);
    board[0][1].setContents(SquareContents.BLACK);
    board[0][2].setContents(SquareContents.BLACK);
    board[0][3].setContents(SquareContents.BLACK);
    board[0][4].setContents(SquareContents.BLACK);
    board[0][5].setContents(SquareContents.BLACK);
    board[0][6].setContents(SquareContents.BLACK);
    board[0][7].setContents(SquareContents.BLACK);
    board[1][0].setContents(SquareContents.BLACK);
    board[1][1].setContents(SquareContents.WHITE);
    board[1][2].setContents(SquareContents.WHITE);
    board[1][3].setContents(SquareContents.WHITE);
    board[1][4].setContents(SquareContents.WHITE);
    board[1][5].setContents(SquareContents.WHITE);
    board[1][6].setContents(SquareContents.BLACK);
    board[1][7].setContents(SquareContents.WHITE);
    board[2][0].setContents(SquareContents.BLACK);
    board[2][1].setContents(SquareContents.WHITE);
    board[2][2].setContents(SquareContents.WHITE);
    board[2][3].setContents(SquareContents.WHITE);
    board[2][4].setContents(SquareContents.WHITE);
    board[2][5].setContents(SquareContents.BLACK);
    board[2][6].setContents(SquareContents.WHITE);
    board[2][7].setContents(SquareContents.WHITE);
    board[3][0].setContents(SquareContents.BLACK);
    board[3][1].setContents(SquareContents.WHITE);
    board[3][2].setContents(SquareContents.WHITE);
    board[3][3].setContents(SquareContents.WHITE);
    board[3][4].setContents(SquareContents.BLACK);
    board[3][5].setContents(SquareContents.WHITE);
    board[3][6].setContents(SquareContents.WHITE);
    board[3][7].setContents(SquareContents.WHITE);
    board[4][0].setContents(SquareContents.BLACK);
    board[4][1].setContents(SquareContents.WHITE);
    board[4][2].setContents(SquareContents.WHITE);
    board[4][3].setContents(SquareContents.BLACK);
    board[4][4].setContents(SquareContents.WHITE);
    board[4][5].setContents(SquareContents.WHITE);
    board[4][6].setContents(SquareContents.WHITE);
    board[4][7].setContents(SquareContents.WHITE);
    board[5][0].setContents(SquareContents.BLACK);
    board[5][1].setContents(SquareContents.WHITE);
    board[5][2].setContents(SquareContents.BLACK);
    board[5][3].setContents(SquareContents.WHITE);
    board[5][4].setContents(SquareContents.WHITE);
    board[5][5].setContents(SquareContents.WHITE);
    board[5][6].setContents(SquareContents.WHITE);
    board[5][7].setContents(SquareContents.WHITE);
    board[6][0].setContents(SquareContents.BLACK);
    board[6][1].setContents(SquareContents.BLACK);
    board[6][2].setContents(SquareContents.WHITE);
    board[6][3].setContents(SquareContents.WHITE);
    board[6][4].setContents(SquareContents.WHITE);
    board[6][5].setContents(SquareContents.WHITE);
    board[6][6].setContents(SquareContents.WHITE);
    board[6][7].setContents(SquareContents.WHITE);
    board[7][0].setContents(SquareContents.BLACK);
    board[7][1].setContents(SquareContents.WHITE);
    board[7][2].setContents(SquareContents.WHITE);
    board[7][3].setContents(SquareContents.WHITE);
    board[7][4].setContents(SquareContents.WHITE);
    board[7][5].setContents(SquareContents.WHITE);
    board[7][6].setContents(SquareContents.WHITE);
    
    TestAbstractOtelloAi.createList(validMoves, new Move(7, 7));
    
    assertTrue("Returns stable move: Last valid move",
               AiStrategies.stableMoves(board, validMoves, 
                                        SquareContents.BLACK).contains(new Move(7, 7)));    
  }
  
  /**
   * Tests the partialStableMoves method in the AiStrategies class.
   * 
   * Starts with a board as follows:
   * BWW?B     
   *   WW     
   *  B W     
   *  W W     
   *  ? W     
   *    W? B
   *    W?WB
   * ?WB??WB
   * 
   */

  @Test
  public void testPartialStableMoves()
  {
    Square[][] board = TestAbstractOtelloAi.createEmptyBoard();
    board[0][0].setContents(SquareContents.BLACK);
    board[0][1].setContents(SquareContents.WHITE);
    board[0][2].setContents(SquareContents.WHITE);
    board[0][4].setContents(SquareContents.BLACK);
    board[1][2].setContents(SquareContents.WHITE);
    board[1][3].setContents(SquareContents.WHITE);
    board[2][1].setContents(SquareContents.BLACK);
    board[2][3].setContents(SquareContents.WHITE);
    board[3][1].setContents(SquareContents.WHITE);
    board[3][3].setContents(SquareContents.WHITE);
    board[4][3].setContents(SquareContents.WHITE);
    board[5][3].setContents(SquareContents.WHITE);
    board[5][7].setContents(SquareContents.BLACK);
    board[6][3].setContents(SquareContents.WHITE);
    board[6][6].setContents(SquareContents.WHITE);
    board[6][7].setContents(SquareContents.BLACK);
    board[7][1].setContents(SquareContents.WHITE);
    board[7][2].setContents(SquareContents.BLACK);
    board[7][6].setContents(SquareContents.WHITE);
    board[7][7].setContents(SquareContents.BLACK);

    /*
    for (int i = 0; i < board.length; i++)
    {
      for (int j = 0; j < board[i].length; j++)
      {
        System.out.print(board[i][j]);
      }
      
      System.out.println();
    }
    */
    
    List<Move> validMoves = new ArrayList<Move>();
    TestAbstractOtelloAi.createList(validMoves, new Move(0, 3), new Move(4, 1), new Move(5, 5),
                                                new Move(6, 5), new Move(7, 0), new Move(7, 5),
                                                new Move(7, 6));

    List<Move> expectedMoves = new ArrayList<Move>();
    TestAbstractOtelloAi.createList(expectedMoves, new Move(0, 3), new Move(7, 6));
    
    // There are two moves that flip two stable pieces
    assertTrue("Returns moves that flip two stable pieces", 
               expectedMoves.containsAll(AiStrategies.partialStableMoves(board, 
                                                                         validMoves, 
                                                                         SquareContents.BLACK)));
    
    // Tests that partially stable moves to C or X squares do not give away corners
    //  ?WWWWWB
    //   W    W
    //    B   B        (0,1) is partially stable but does not give away the (0,0) corner
    //        W
    //        ?        (4,7) is partially stable but gives away the (7,7) corner
    //       WB
    // BBBBBW B
    // BBBBBBB
    board = TestAbstractOtelloAi.createEmptyBoard();
    board[0][2].setContents(SquareContents.WHITE);
    board[0][3].setContents(SquareContents.WHITE);
    board[0][4].setContents(SquareContents.WHITE);
    board[0][5].setContents(SquareContents.WHITE);
    board[0][6].setContents(SquareContents.WHITE);
    board[0][7].setContents(SquareContents.BLACK);
    board[1][2].setContents(SquareContents.WHITE);
    board[1][7].setContents(SquareContents.WHITE);
    board[2][3].setContents(SquareContents.BLACK);
    board[2][7].setContents(SquareContents.BLACK);
    board[3][7].setContents(SquareContents.WHITE);
    board[5][6].setContents(SquareContents.WHITE);
    board[5][7].setContents(SquareContents.BLACK);
    board[6][0].setContents(SquareContents.BLACK);
    board[6][1].setContents(SquareContents.BLACK);
    board[6][2].setContents(SquareContents.BLACK);
    board[6][3].setContents(SquareContents.BLACK);
    board[6][4].setContents(SquareContents.BLACK);
    board[6][5].setContents(SquareContents.WHITE);
    board[6][7].setContents(SquareContents.BLACK);
    board[7][0].setContents(SquareContents.BLACK);
    board[7][1].setContents(SquareContents.BLACK);
    board[7][2].setContents(SquareContents.BLACK);
    board[7][3].setContents(SquareContents.BLACK);
    board[7][4].setContents(SquareContents.BLACK);
    board[7][5].setContents(SquareContents.BLACK);
    board[7][6].setContents(SquareContents.BLACK);
    
    TestAbstractOtelloAi.createList(validMoves, new Move(0, 1));
    assertTrue("Accepts partially stable move that does not give away corner",
               AiStrategies.partialStableMoves(board, validMoves, 
                                               SquareContents.BLACK).contains(new Move(0, 1)));
    
    assertFalse("Rejects partially stable move that gives away corner",
                AiStrategies.partialStableMoves(board, validMoves, 
                                                SquareContents.BLACK).contains(new Move(4, 7)));
  }
  
  /**
   * Returns a List<Move> containing all valid Move instances.
   * 
   * @param board The Square[][] representing the Otello board to find moves in.
   * 
   * @return moves The List<Move> containing all valid moves.
   *  
   */
  
  private static List<Move> allMoves(Square[][] board)
  {
    // Add all squares to list
    List<Move> moves = new ArrayList<Move>();
    
    for (int row = 0; row < board.length; row++)
    {
      for (int column = 0; column < board[row].length; column++)
      {
        moves.add(new Move(row, column));
      }
    }
    
    return moves;
  }
}